package apis

import (
	"sort"
	"strconv"
	"strings"
	"unsafe"

	ckeadmv1beta1 "ckeadm/cmd/apis/v1alpha1"
	"ckeadm/constants"
	"ckeadm/version"
)

func Convert_ckeadm_Configuration_To_v1beta1_Configuration(in *CkeAdmKubernetesConfig) (out *ckeadmv1beta1.CkeCluster) {
	return
}

//todo 修改参数获取
func Convert_component_Configuration_To_ckeadm_Configuration(cluster *ckeadmv1beta1.CkeCluster, nodes *[]ckeadmv1beta1.CkeNode,
	components *[]ckeadmv1beta1.CkeComponent) (out *CkeAdmKubernetesConfig) {
	if cluster == nil || nodes == nil || components == nil {
		return nil
	}
	sortnodes := ckeNodeSort(*nodes)
	sort.Sort(sortnodes)
	var in = cluster.Spec
	out = &CkeAdmKubernetesConfig{
		ClusterName:       *(*string)(unsafe.Pointer(&cluster.Name)),
		Version:           *(*string)(unsafe.Pointer(&in.Version)),
		ClusterParameters: *(*CkeClusterPrameter)(unsafe.Pointer(&in.ClusterParameters)),
		Registry:          *(*CkeRegistry)(unsafe.Pointer(&in.Registry)),
		ClusterVips:       *(*[]CkeClusterVip)(unsafe.Pointer(&in.ClusterVips)),
		ClusterVip:        *(*CkeClusterVip)(unsafe.Pointer(&in.ClusterVip)),
		Etcd:              *(*CkeEtcd)(unsafe.Pointer(&in.Etcd)),
		Docker:            *(*CkeDocker)(unsafe.Pointer(&in.Docker)),
		Dns:               *(*CkeDns)(unsafe.Pointer(&in.Dns)),
		Plugins:           *(*[]CkeAddons)(unsafe.Pointer(&in.Plugins)),
		Addons:            *(*[]CkeAddons)(unsafe.Pointer(&in.Addons)),
		ExtraArgs:         *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs)),
		DockerPreconfiged: *(*[]string)(unsafe.Pointer(&in.DockerPreconfiged)),
		AnsibleSshPort:    *(*int)(unsafe.Pointer(&in.AnsibleSshPort)),
		Nodes:             []CkeConfigNode{},
		Calico:            CkeCalico{Mtu: 1430}, // default
		WatchConfig:       nil,
		Components:        *components,
	}
	for _, plugin := range in.Plugins {
		if mtu, err := strconv.Atoi(plugin.Parameters["mtu"]); plugin.Name == "calico" && err == nil {
			out.Calico.Mtu = mtu
			break
		}
	}
	for _, node := range sortnodes {
		if skip, err := strconv.ParseBool(node.Annotations[constants.NodeSkipAnnotations]); err == nil && skip {
			continue
		}
		out.Nodes = append(out.Nodes, CkeConfigNode{
			Address:     node.Spec.Address,
			Name:        node.ObjectMeta.Name,
			Roles:       AutoSetRoles(node.Spec.Roles),
			Labels:      node.Spec.Labels,
			Taints:      node.Spec.Taints,
			MaxPods:     int(node.Spec.MaxPods),
			Annotations: *(*map[string]string)(&node.ObjectMeta.Annotations),
		})
	}
	return out
}

func Convert_v1beta1_Configuration_To_ckeadm_Configuration(cluster *ckeadmv1beta1.CkeCluster, nodes *[]ckeadmv1beta1.CkeNode) (out *CkeAdmKubernetesConfig) {
	if cluster == nil || nodes == nil {
		return nil
	}
	sortnodes := ckeNodeSort(*nodes)
	sort.Sort(sortnodes)
	var in = cluster.Spec
	out = &CkeAdmKubernetesConfig{
		ClusterName:       *(*string)(unsafe.Pointer(&cluster.Name)),
		Version:           *(*string)(unsafe.Pointer(&in.Version)),
		ClusterParameters: *(*CkeClusterPrameter)(unsafe.Pointer(&in.ClusterParameters)),
		Registry:          *(*CkeRegistry)(unsafe.Pointer(&in.Registry)),
		ClusterVips:       *(*[]CkeClusterVip)(unsafe.Pointer(&in.ClusterVips)),
		ClusterVip:        *(*CkeClusterVip)(unsafe.Pointer(&in.ClusterVip)),
		Etcd:              *(*CkeEtcd)(unsafe.Pointer(&in.Etcd)),
		Docker:            *(*CkeDocker)(unsafe.Pointer(&in.Docker)),
		Dns:               *(*CkeDns)(unsafe.Pointer(&in.Dns)),
		Plugins:           *(*[]CkeAddons)(unsafe.Pointer(&in.Plugins)),
		Addons:            *(*[]CkeAddons)(unsafe.Pointer(&in.Addons)),
		ExtraArgs:         *(*map[string]string)(unsafe.Pointer(&in.ExtraArgs)),
		DockerPreconfiged: *(*[]string)(unsafe.Pointer(&in.DockerPreconfiged)),
		AnsibleSshPort:    *(*int)(unsafe.Pointer(&in.AnsibleSshPort)),
		Nodes:             []CkeConfigNode{},
		Calico:            CkeCalico{Mtu: 1430}, // default
		WatchConfig:       nil,
	}
	for _, plugin := range in.Plugins {
		if mtu, err := strconv.Atoi(plugin.Parameters["mtu"]); plugin.Name == "calico" && err == nil {
			out.Calico.Mtu = mtu
			break
		}
	}
	for _, node := range sortnodes {
		if skip, err := strconv.ParseBool(node.Annotations[constants.NodeSkipAnnotations]); err == nil && skip {
			continue
		}
		out.Nodes = append(out.Nodes, CkeConfigNode{
			Address:     node.Spec.Address,
			Name:        node.ObjectMeta.Name,
			Roles:       AutoSetRoles(node.Spec.Roles),
			Labels:      node.Spec.Labels,
			Taints:      node.Spec.Taints,
			MaxPods:     int(node.Spec.MaxPods),
			Annotations: *(*map[string]string)(&node.ObjectMeta.Annotations),
		})
	}
	return out
}

func LoadConfigurationDefault() *ckeadmv1beta1.CkeCluster {
	var config = new(ckeadmv1beta1.CkeCluster)

	config.Spec.ClusterParameters.ImageManifestEnabled = true
	config.Spec.AnsibleSshPort = 22
	config.Spec.Version = version.VERSION

	return config
}

func AutoSetRoles(roles []string) []string {
	var rolemap = make(map[string]bool, 0)
	var newroles []string

	// []string -> map
	for item := range roles {
		rolemap[roles[item]] = true
	}

	// auto set roles, master must be worker
	if rolemap["master"] {
		rolemap["worker"] = true
	}

	// map -> []string
	for role := range rolemap {
		newroles = append(newroles, role)
	}

	return newroles
}

type ckeNodeSort []ckeadmv1beta1.CkeNode

func (ms ckeNodeSort) Len() int {
	return len(ms)
}

func (ms ckeNodeSort) Swap(i, j int) {
	ms[i], ms[j] = ms[j], ms[i]
}

//按键排序
func (ms ckeNodeSort) Less(i, j int) bool {
	nodei := ms[i]
	nodej := ms[j]
	namei := nodei.Name
	namej := nodej.Name
	ismgti := strings.Contains(namei, "mgt") || strings.Contains(namei, "master")
	ismgtj := strings.Contains(namej, "mgt") || strings.Contains(namej, "master")
	if ismgti && ismgtj {
		var mgtinum, mgtjnum string
		if strings.Contains(namei, "mgt") {
			mgtinum = strings.ReplaceAll(namei, "mgt", "")
		} else if strings.Contains(namei, "master") {
			mgtinum = strings.ReplaceAll(namei, "master", "")
		}
		if strings.Contains(namej, "mgt") {
			mgtjnum = strings.ReplaceAll(namej, "mgt", "")
		} else if strings.Contains(namej, "master") {
			mgtjnum = strings.ReplaceAll(namej, "master", "")
		}
		inum, err := strconv.Atoi(mgtinum)
		if err != nil {
			return mgtinum < mgtjnum
		}
		jnum, err := strconv.Atoi(mgtjnum)
		if err != nil {
			return mgtinum < mgtjnum
		}
		return inum < jnum
	} else if ismgti && !ismgtj {
		return true
	} else if !ismgti && ismgtj {
		return false
	} else {
		return namei < namej
	}
}
